﻿using SuperBasic.FrontEnd.Inter;
using SuperBasic.FrontEnd.Symbols;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SuperBasic.FrontEnd.Parser
{
    class Statments
    {
        public Statments()
        {

        }
    }


    class RValue : Stmt
    {
        public RValue(Scopable parent, TokenReader reader, Symbol lValue) : base(parent, reader)
        {

        }
    }

    //Expression
    //Except for ""
    //Mumber
    //Till Line End
    class Expr : Stmt
    {
        /*
         *  expr ->  expr + term
         *       |   expr - term
         *      
         *  term ->  term * factor
         *       |   term / factor
         *  
         *  factor -> (expr)
         *          | num
         *          | id
        */
        // lvalue = rvalue
        // 
        //      lvalue -> id
        //      rvalue -> expr

        //List<Token> exprs;

        public Expr(Scopable parent, TokenReader reader, Symbol lValue) : base(parent, reader)
        {
            #region
            //exprs = new List<Token>();
            //Token cur = reader.Read();
            //cur = reader.Read();

            ////if no a literal token, must except a number or a identity
            //if (ExceptGrammar(cur, Tag.IDENTITY, Tag.NUMBER, Tag.LITERAL, Tag.OPERATOR))
            //{
            //    exprs.Add(cur);
            //    do
            //    {
            //        cur = reader.Read();
            //        if (Except(cur, Tag.OPERATOR))
            //        {
            //            exprs.Add(cur);
            //            cur = reader.Read();
            //            if (Except(cur, Tag.IDENTITY, Tag.LITERAL, Tag.NUMBER))
            //            {
            //                exprs.Add(cur);
            //            }
            //        }
            //        else if(cur.TokenTag == Tag.LINE_END)
            //        {
            //            break;
            //        }
            //    } while (Except(cur, Tag.IDENTITY, Tag.NUMBER, Tag.LITERAL, Tag.OPERATOR));
            //}

            //// 9 + 5 - 2
            //// 9 + 5 - 2
            //// 2 - 5 + 9
            //// 2 5 - 9 +
            //// 9 5 + 2 -

            //// 6 * 5 + 9 - 2
            //// 2 - 9 + 5 * 6
            //// 2 9 - 5 6 * +

            //Stack<Token> expr = new Stack<Token>();
            //Stack<Operator> op = new Stack<Operator>();

            //foreach (var item in exprs)
            //{
            //    if(Except(item, Tag.NUMBER, Tag.IDENTITY, Tag.LITERAL))
            //    {
            //        expr.Push(item);
            //    }
            //    else if (Except(item, Tag.OPERATOR))
            //    {
            //        Operator opt = item as Operator;

            //        while(op.Count > 0)
            //        {
            //            if(isLargeEq(op.Peek().Level, opt.Level))
            //            {
            //                expr.Push(op.Pop());
            //            }
            //            else
            //            {
            //                op.Push(opt);
            //                break;
            //            }
            //        }
            //    }

            //}

            //while (op.Count > 0)
            //{
            //    expr.Push(op.Pop());
            //}

            //Stack<Token> result = new Stack<Token>();

            //while (expr.Count > 0)
            //{
            //    cur = expr.Pop();
            //    if (Except(cur, Tag.IDENTITY, Tag.IDENTITY, Tag.LITERAL))
            //    {
            //        result.Push(cur);
            //    }

            //    if(Except(cur, Tag.OPERATOR))
            //    {

            //    }
            //}

            #endregion
        }

        public bool isLargeEq(Operators op1, Operators op2)
        {
            return (int)op1 <= (int)op2;
        }
    }

    // Function [ID] [Params] [Ret-Param]
    //  Stmt
    // End Function

    // Sub [ID] [Params]
    //  Stmt
    // End Function
    class Produce : Stmt
    {
        public Produce(Scopable parent, TokenReader reader) : base(parent, reader)
        {
        }
    }

    // Do
    //  [Stmt]
    // Loop While [Expr]
    class Do : Stmt
    {
        public Do(Scopable parent, TokenReader reader) : base(parent, reader)
        {
        }
    }

    // (Do )While [Expr]
    //  [Stmt]
    // Loop/Wend
    class While : Stmt
    {
        public While(Scopable parent, TokenReader reader) : base(parent, reader)
        {
        }
    }

    // If ([Expr]) Then 
    //  [Stmt]
    // End If
    class If : Stmt
    {
        public If(Scopable parent, TokenReader reader) : base(parent, reader)
        {

        }

    }

    // Dim (ID) As (TYPE) [=] 
    class Dim : Stmt
    {
        public Dim(Scopable parent, TokenReader reader) : base(parent, reader)
        {
            #region
            //Token cur = reader.Read();
            //if(ExceptGrammar(cur, Tag.IDENTITY))
            //{
            //    Word idName = cur as Word;

            //    if(idName.Line == 0 && idName.Pos == 0)
            //    {
            //        throw new TokenPreserveException(idName);
            //    }
            //    cur = reader.Read();
            //    if(ExceptGrammar(cur, Tag.AS))
            //    {
            //        cur = reader.Read();
            //        if(ExceptGrammar(cur, Tag.IDENTITY))
            //        {
            //            Word idType = cur as Word;
            //            Tag next = reader.ReadNext().TokenTag;

            //            Symbol s;
            //            string name = idName.lexeme;
            //            Tokens.Type t = ;
            //            object data = null;
            //            if (Enum.GetNames(typeof(VarType)).Contains(idType.lexeme))
            //            {
            //                t = (VarType)Enum.Parse(typeof(VarType), idType.lexeme);
            //            }

            //            s = new Symbol(name, t, data);
            //            Env().Add(name, s);

            //            if (next == Tag.EQUAL)
            //            {
            //                data = new Expr(this, reader, s);
            //            }
            //            AddCode(new Code(idName.Line, idName.Pos, new Code.Operator(Code.Operator_.def), new Symbol(name, VarType.String), null, null));
            //        }
            //    }
            //}
            #endregion
            //old
        }
    }

    // Stmt
    class Stmt : Scopable, Codiliazition
    {
        SymbolTable st;
        List<Code> code;
        TokenReader reader;
        public Stmt(Scopable parent, TokenReader reader)
        {
            st = new SymbolTable(parent?.Env());
            code = new List<Code>();
            this.reader = reader;
        }

        public void AddSymbol(Symbol sb)
        {
            st.Add(sb.Identity, sb);
        }

        public void AddCode(IEnumerable<Code> codes)
        {
            code.AddRange(codes);
        }

        public void AddCode(Code code)
        {
            this.code.Add(code);
        }

        public SymbolTable Env()
        {
            return st;
        }

        public IEnumerable<Code> gen()
        {
            return code;
        }

        public void doParser()
        {
            //Token cur = reader.Read();
            //do
            //{
            //    switch (cur.TokenTag)
            //    {

            //    }
            //} while (cur != null);
        }

        public bool Except(Token curToken, params Tag[] tags)
        {
            if (tags.Contains(curToken.TokenTag))
                return true;
            else
            {
                return false;
            }
        }

        public bool ExceptGrammar(Token curToken, params Tag[] tags)
        {
            if (tags.Contains(curToken.TokenTag))
                return true;
            else
            {
                throw new TokenWrongException(curToken, tags);
            }
        }
    }

    class Block : Scopable
    {
        SymbolTable st;
        Stmt codes;
        public Block()
        {
            st = new SymbolTable(null);
        }

        public SymbolTable Env()
        {
            return st;
        }


        public IEnumerable<Code> Parser(Parser parser)
        {
            codes = new Stmt(this, parser.Reader);
            codes.doParser();
            return codes.gen();
        }
    }

    interface Scopable
    {
        SymbolTable Env();
    }

    interface Codiliazition
    {
        IEnumerable<Code> gen();
    }

}
